from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import (
    TimeoutException, StaleElementReferenceException,
    ElementClickInterceptedException, ElementNotInteractableException,
    NoSuchElementException, WebDriverException, NoSuchFrameException
)
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.remote.webelement import WebElement
from typing import List, Union

from utils.decorator import retry
from utils.logger import Log as log
from utils.read_element import ElementYamlUtils
from utils.times import strftime
from utils.path_manager import path_manager

import time
import os

log = log()


class BasePage(object):
    """页面基类，提供与页面交互的通用方法"""

    def __init__(self, driver):
        self.driver = driver
        self.action = ActionChains(self.driver)

    def highlight_element(self, element, duration=0.5):
        """高亮显示元素"""
        try:
            self.execute_js(
                "arguments[0].setAttribute('style',arguments[1]);", element,
                "border:2px solid red;"
            )
            return True
        except Exception as e:
            log.warning(f"高亮元素失败：{str(e)}")
            return False

    @retry(exceptions=(StaleElementReferenceException, TimeoutException))
    def find_element(self, locator, timeout=None, poll_frequency=0.5, highlight=True) -> WebElement:
        """查找单个元素，等待其可见"""
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            if hasattr(locator, 'timeout') and locator.timeout is not None:
                timeout = locator.timeout

            log.info(f"查找元素：{locator}，等待时间：{timeout}秒")
            element = self.wait_for_element_visible(locator, timeout, poll_frequency)
            # 高亮显示元素，便于调试
            if highlight:
                self.highlight_element(element)

            log.info(f"成功查找元素：{str(locator)}")
            return element
        except TimeoutException:
            log.error(f"查找元素超时：{locator}，等待时间：{timeout}秒")
            # 截图记录失败状态
            self.shot_file(f"find_element_timeout_{int(time.time())}")
            raise
        except Exception as e:
            log.error(f"查找元素异常：{locator}，异常信息：{str(e)}")
            # 截图记录失败状态
            self.shot_file(f"find_element_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, TimeoutException))
    def find_elements(self, locator, timeout=None, poll_frequency=0.5) -> List[WebElement]:
        """查找多个元素，等待至少一个元素可见"""
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            if hasattr(locator, 'timeout') and locator.timeout is not None:
                timeout = locator.timeout

            log.info(f"查找元素集合：{locator}，等待时间：{timeout}秒")
            elements = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.presence_of_all_elements_located((locator.locator_type, locator.locator_value)))

            # 过滤出可见的元素
            visible_elements = [e for e in elements if e.is_displayed()]

            log.info(f"成功查找元素集合：{str(locator)}，找到 {len(elements)} 个元素，其中 {len(visible_elements)} 个可见")
            return elements
        except TimeoutException:
            log.warning(f"查找元素集合超时：{str(locator)}，等待时间：{timeout}秒")
            # 截图记录失败状态
            self.shot_file(f"find_elements_timeout_{int(time.time())}")
            return []
        except Exception as e:
            log.error(f"查找元素集合异常：{str(locator)}，异常信息：{str(e)}")
            # 截图记录失败状态
            self.shot_file(f"find_elements_error_{int(time.time())}")
            return []

    @retry(exceptions=(TimeoutException, WebDriverException))
    def open_url(self, url, page_load_timeout=30):
        """打开网址并验证，支持重试"""
        log.info(f"打开URL：{url}，超时时间：{page_load_timeout}秒")
        try:
            self.driver.set_page_load_timeout(page_load_timeout)
            self.driver.get(url)
            # 等待页面加载完成
            self.wait_for_page_loaded(timeout=page_load_timeout)
            return True
        except TimeoutException:
            # 执行Javascript来停止页面加载
            try:
                self.driver.execute_script('window.stop()')
                log.warning(f"打开{url}超时，已停止加载")
            except Exception as js_e:
                log.error(f"停止页面加载失败：{str(js_e)}")

            # 截图记录超时状态
            self.shot_file(f"url_timeout_{int(time.time())}")
            # 由于使用了retry装饰器，这里的异常会被捕获并重试
            raise TimeoutException(f"打开URL超时：{url}")
        except WebDriverException as e:
            log.error(f"打开网页异常：{url}，异常信息：{str(e)}")
            # 截图记录错误状态
            self.shot_file(f"url_error_{int(time.time())}")
            # 由于使用了retry装饰器，这里的异常会被捕获并重试
            raise
        except Exception as e:
            log.error(f"打开网页未知异常：{url}，异常信息：{str(e)}")
            # 截图记录错误状态
            self.shot_file(f"url_unknown_error_{int(time.time())}")
            return False

    def pause(self, seconds: float):
        """等待固定的时间
        
        Args:
            seconds: 等待的秒数
        """
        if seconds <= 0:
            log.warning(f"暂停时间必须大于0，当前值：{seconds}，已调整为0.1秒")
            seconds = 0.1
        log.info(f"暂停{seconds}秒")
        time.sleep(seconds)

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def input(self, locator, text, clear_first=True, click_first=True):
        """输入文本，可选是否先清空和点击
        
        Args:
            locator: 元素定位器
            text: 要输入的文本
            clear_first: 是否先清空文本框
            click_first: 是否先点击元素
        """
        try:
            log.info(f"输入文本：{locator}，文本内容：{text}，是否先清空：{clear_first}，是否先点击：{click_first}")
            ele = self.find_element(locator)

            # 先点击元素，确保元素获得焦点
            if click_first:
                try:
                    ele.click()
                except Exception as e:
                    log.warning(f"点击元素失败：{str(e)}，尝试使用JavaScript点击")
                    self.driver.execute_script("arguments[0].click();", ele)

            if clear_first:
                try:
                    ele.clear()
                    # 确保清空成功
                    if ele.get_attribute('value'):
                        log.warning(f"常规清空不完全，尝试使用Backspace键清空")
                        # 使用Backspace键清空
                        current_value = ele.get_attribute('value')
                        for _ in range(len(current_value)):
                            ele.send_keys(Keys.BACKSPACE)
                except Exception as e:
                    log.warning(f"清空文本框失败：{str(e)}，尝试使用JS清空")
                    self.execute_js('arguments[0].value = ""', ele)

            # 检查清空是否成功
            if clear_first and ele.get_attribute('value'):
                log.warning(f"清空文本框失败，尝试使用另一种方式清空")
                # 尝试选择全部文本并删除
                try:
                    ele.send_keys(Keys.CONTROL + "a")
                    ele.send_keys(Keys.DELETE)
                except Exception as e:
                    log.warning(f"使用全选删除清空失败：{str(e)}")

            # 输入文本
            ele.send_keys(text)

            # 验证输入是否成功
            actual_value = ele.get_attribute('value')
            if text not in actual_value and actual_value not in text:
                log.warning(f"输入验证失败，期望：{text}，实际：{actual_value}，尝试使用JavaScript设置")
                self.execute_js(f'arguments[0].value = "{text}"', ele)

            log.info(f"文本框:{locator} 输入文本:{text} 成功")
            return True
        except ElementNotInteractableException as e:
            log.error(f"元素不可交互：{locator}，异常信息：{str(e)}")
            self.shot_file(f"input_not_interactable_{int(time.time())}")
            raise
        except Exception as e:
            log.error(f"输入文本失败：{locator}，文本：{text}，异常信息：{str(e)}")
            self.shot_file(f"input_error_{int(time.time())}")
            raise

    @retry(
        exceptions=(StaleElementReferenceException, ElementClickInterceptedException, ElementNotInteractableException))
    def click(self, locator, timeout=None, use_js_if_failed=True):
        """点击元素，带重试机制
        
        Args:
            locator: 元素定位器
            timeout: 等待超时时间，默认使用全局配置
            use_js_if_failed: 如果常规点击失败，是否尝试使用JavaScript点击
        """
        try:
            log.info(f"点击元素：{locator}")
            # 先等待元素可点击
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            self.wait_for_element_clickable(locator, timeout)

            # 查找元素并高亮显示（调试时有用）
            ele = self.find_element(locator, highlight=True)

            # 尝试常规点击
            try:
                ele.click()
                log.info(f"点击元素成功：{locator}")
                return True
            except ElementClickInterceptedException as e:
                # 元素被遮挡，尝试JS点击
                log.warning(f"元素被遮挡，尝试JS点击：{str(e)}")
                if use_js_if_failed:
                    self.execute_js('arguments[0].click()', ele)
                    log.info(f"通过JS点击元素成功：{locator}")
                    return True
                else:
                    log.error(f"元素被遮挡且未启用JS点击：{locator}")
                    self.shot_file(f"click_intercepted_{int(time.time())}")
                    raise
            except ElementNotInteractableException as e:
                # 元素不可交互，尝试JS点击
                log.warning(f"元素不可交互，尝试JS点击：{str(e)}")
                if use_js_if_failed:
                    self.execute_js('arguments[0].click()', ele)
                    log.info(f"通过JS点击元素成功：{locator}")
                    return True
                else:
                    log.error(f"元素不可交互且未启用JS点击：{locator}")
                    self.shot_file(f"click_not_interactable_{int(time.time())}")
                    raise
            except Exception as e:
                # 其他异常，尝试滚动到元素位置后点击
                log.warning(f"点击失败，尝试滚动到元素位置：{str(e)}")
                try:
                    self.execute_js('arguments[0].scrollIntoView({block: "center"})', ele)
                    time.sleep(0.5)  # 等待滚动完成
                    ele.click()
                    log.info(f"滚动后点击元素成功：{locator}")
                    return True
                except Exception as scroll_e:
                    log.error(f"滚动后点击仍然失败：{str(scroll_e)}")
                    # 最后尝试JS点击
                    if use_js_if_failed:
                        try:
                            self.execute_js('arguments[0].click()', ele)
                            log.info(f"最终通过JS点击元素成功：{locator}")
                            return True
                        except Exception as js_e:
                            log.error(f"所有点击方法均失败：{str(js_e)}")
                            self.shot_file(f"click_all_failed_{int(time.time())}")
                            raise
                    else:
                        self.shot_file(f"click_failed_{int(time.time())}")
                        raise
        except Exception as e:
            # 捕获所有其他未处理的异常
            log.error(f"点击元素失败（未预期的异常）：{locator}，异常信息：{str(e)}")
            self.shot_file(f"click_unexpected_{int(time.time())}")
            raise

    @retry()
    def click_area(self, x, y):
        """点击屏幕上任意位置
        
        Args:
            x: 横坐标
            y: 纵坐标
        """
        try:
            log.info(f"点击屏幕坐标：({x}, {y})")
            # 确保 action 属性存在
            if not hasattr(self, 'action') or self.action is None:
                self.action = ActionChains(self.driver)

            # 先移动到原点，然后移动到指定位置并点击
            self.action.move_by_offset(0, 0).move_by_offset(x, y).click().perform()
            log.info(f"点击屏幕坐标成功：({x}, {y})")
            return True
        except Exception as e:
            log.error(f"点击屏幕坐标失败：({x}, {y})，异常信息：{str(e)}")
            self.shot_file(f"click_area_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def right_click(self, locator):
        """
        右键单击元素
        
        Args:
            locator: 元素定位器
        """
        try:
            log.info(f"右键点击元素：{locator}")
            ele = self.find_element(locator, highlight=True)
            ActionChains(self.driver).context_click(ele).perform()
            log.info(f"右键点击元素成功：{locator}")
            return True
        except Exception as e:
            log.error(f"右键点击元素失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"right_click_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def double_click(self, locator):
        """
        双击元素
        
        Args:
            locator: 元素定位器
        """
        try:
            log.info(f"双击元素：{locator}")
            ele = self.find_element(locator, highlight=True)
            ActionChains(self.driver).double_click(ele).perform()
            log.info(f"双击元素成功：{locator}")
            return True
        except Exception as e:
            log.error(f"双击元素失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"double_click_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def click_link_text(self, text):
        """
        单击链接文本中的元素
        
        Args:
            text: 链接文本内容
        """
        try:
            log.info(f"点击链接文本：{text}")
            element = self.driver.find_element(By.PARTIAL_LINK_TEXT, text)
            element.click()
            log.info(f"点击链接文本成功：{text}")
            return True
        except NoSuchElementException:
            log.error(f"未找到链接文本：{text}")
            self.shot_file(f"link_text_not_found_{int(time.time())}")
            raise
        except Exception as e:
            log.error(f"点击链接文本失败：{text}，异常信息：{str(e)}")
            self.shot_file(f"click_link_text_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def move_to_element(self, locator):
        """移动到指定元素上(悬停)
        
        Args:
            locator: 元素定位器
        """
        try:
            log.info(f"移动到元素上：{locator}")
            ele = self.find_element(locator, highlight=True)
            # 确保 action 属性存在
            if not hasattr(self, 'action') or self.action is None:
                self.action = ActionChains(self.driver)
            self.action.move_to_element(ele).perform()
            log.info(f"成功移动到元素上：{locator}")
            return True
        except Exception as e:
            log.error(f"移动到元素上失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"move_to_element_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def move_click_element(self, locator):
        """移动到元素并点击
        
        Args:
            locator: 元素定位器
        """
        try:
            log.info(f"移动到元素并点击：{locator}")
            ele = self.find_element(locator, highlight=True)
            # 确保 action 属性存在
            if not hasattr(self, 'action') or self.action is None:
                self.action = ActionChains(self.driver)
            self.action.move_to_element(ele).click().perform()
            log.info(f"成功移动到元素并点击：{locator}")
            return True
        except ElementClickInterceptedException as e:
            log.warning(f"元素被遮挡，尝试JS点击：{str(e)}")
            self.execute_js('arguments[0].click()', ele)
            log.info(f"通过JS点击元素成功：{locator}")
            return True
        except Exception as e:
            log.error(f"移动到元素并点击失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"move_click_element_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def drag_element_from_to(self, source_locator, target_locator):
        """真实的拖拽操作：使用Selenium ActionChains进行拖拽
        
        Args:
            source_locator: 源元素定位器
            target_locator: 目标元素定位器
            
        Returns:
            bool: 成功返回True，失败抛出异常
        """
        try:
            log.info(f"真实拖拽元素：从 {source_locator} 到 {target_locator}")
            source = self.find_element(source_locator, highlight=True)
            target = self.find_element(target_locator, highlight=True)

            # 确保 action 属性存在
            if not hasattr(self, 'action') or self.action is None:
                self.action = ActionChains(self.driver)

            # 尝试多种真实拖拽方式
            success = False
            
            # 方法1: 标准的drag_and_drop
            try:
                self.action.drag_and_drop(source, target).perform()
                log.info("使用标准drag_and_drop方法")
                success = True
            except Exception as e1:
                log.warning(f"标准drag_and_drop失败: {str(e1)}, 尝试替代方法")
            
            # 方法2: 使用click_and_hold + move_to_element + release
            if not success:
                try:
                    self.action.click_and_hold(source).move_to_element(target).release().perform()
                    log.info("使用click_and_hold + move_to_element + release方法")
                    success = True
                except Exception as e2:
                    log.warning(f"click_and_hold方法失败: {str(e2)}")
                    raise e2

            if success:
                log.info(f"真实拖拽成功：从 {source_locator} 到 {target_locator}")
                return True
            else:
                raise Exception("所有真实拖拽方法都失败了")
        except Exception as e:
            log.error(f"真实拖拽失败：从 {source_locator} 到 {target_locator}，异常信息：{str(e)}")
            self.shot_file(f"real_drag_drop_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def simulate_drag_drop(self, source_locator, target_locator):
        """模拟拖拽操作：使用JavaScript模拟HTML5拖拽事件
        
        Args:
            source_locator: 源元素定位器
            target_locator: 目标元素定位器
            
        Returns:
            bool: 成功返回True，失败抛出异常
        """
        try:
            log.info(f"模拟拖拽元素：从 {source_locator} 到 {target_locator}")
            source = self.find_element(source_locator, highlight=True)
            target = self.find_element(target_locator, highlight=True)

            # 使用JavaScript模拟拖拽事件
            log.info("使用JavaScript模拟拖拽事件")
            self._simulate_drag_drop_with_js(source, target)

            log.info(f"模拟拖拽成功：从 {source_locator} 到 {target_locator}")
            return True
        except Exception as e:
            log.error(f"模拟拖拽失败：从 {source_locator} 到 {target_locator}，异常信息：{str(e)}")
            self.shot_file(f"simulate_drag_drop_error_{int(time.time())}")
            raise

    def _simulate_drag_drop_with_js(self, source_element, target_element):
        """使用JavaScript模拟拖拽事件"""
        js_code = """
        function simulateDragDrop(sourceElement, targetElement) {
            // 创建dragstart事件
            var dragStartEvent = new DragEvent('dragstart', {
                bubbles: true,
                cancelable: true,
                dataTransfer: new DataTransfer()
            });
            dragStartEvent.dataTransfer.setData('text/plain', sourceElement.id);
            
            // 创建dragover事件
            var dragOverEvent = new DragEvent('dragover', {
                bubbles: true,
                cancelable: true,
                dataTransfer: dragStartEvent.dataTransfer
            });
            
            // 创建drop事件
            var dropEvent = new DragEvent('drop', {
                bubbles: true,
                cancelable: true,
                dataTransfer: dragStartEvent.dataTransfer
            });
            
            // 触发事件序列
            sourceElement.dispatchEvent(dragStartEvent);
            targetElement.dispatchEvent(dragOverEvent);
            targetElement.dispatchEvent(dropEvent);
        }
        
        simulateDragDrop(arguments[0], arguments[1]);
        """
        self.driver.execute_script(js_code, source_element, target_element)

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def open_new_window(self, locator, timeout=10):
        """
        打开新窗口并切换到新打开的窗口.
        用法:
        传入一个点击后会跳转的元素
        """
        try:
            # 记录当前窗口句柄
            original_window = self.driver.current_window_handle
            original_handles = set(self.driver.window_handles)

            # 点击元素打开新窗口
            el = self.find_element(locator)
            el.click()
            log.info(f"点击元素打开新窗口：{locator}")

            # 等待新窗口出现
            start_time = time.time()
            new_handle = None

            while time.time() - start_time < timeout:
                current_handles = set(self.driver.window_handles)
                new_handles = current_handles - original_handles

                if new_handles:
                    new_handle = list(new_handles)[0]
                    break

                time.sleep(0.5)

            if new_handle:
                self.driver.switch_to.window(new_handle)
                log.info(f"成功切换到新窗口，标题：{self.driver.title}")
                return True
            else:
                log.error(f"等待新窗口超时，超时时间：{timeout}秒")
                self.shot_file(f"open_new_window_timeout_{int(time.time())}")
                return False

        except Exception as e:
            log.error(f"打开新窗口失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"open_new_window_error_{int(time.time())}")
            # 尝试回到原窗口
            if original_window:
                try:
                    self.driver.switch_to.window(original_window)
                    log.info("已回到原窗口")
                except Exception as switch_error:
                    log.error(f"回到原窗口失败：{str(switch_error)}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def select(self, locator, value):
        """在下拉菜单中根据值选择选项
        
        Args:
            locator: 下拉框元素定位器
            value: 选项的值
        """
        try:
            log.info(f"在下拉框中选择值：{locator}，值：{value}")
            ele = self.find_element(locator, highlight=True)
            Select(ele).select_by_value(value)
            log.info(f"成功在下拉框中选择值：{locator}，值：{value}")
            return True
        except Exception as e:
            log.error(f"在下拉框中选择值失败：{locator}，值：{value}，异常信息：{str(e)}")
            self.shot_file(f"select_by_value_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def select_by_text(self, locator, text):
        """在下拉菜单中根据文本选择选项
        
        Args:
            locator: 下拉框元素定位器
            text: 选项的可见文本
        """
        try:
            log.info(f"在下拉框中选择文本：{locator}，文本：{text}")
            ele = self.find_element(locator, highlight=True)
            Select(ele).select_by_visible_text(text)
            log.info(f"成功在下拉框中选择文本：{locator}，文本：{text}")
            return True
        except Exception as e:
            log.error(f"在下拉框中选择文本失败：{locator}，文本：{text}，异常信息：{str(e)}")
            self.shot_file(f"select_by_text_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def select_by_index(self, locator, index):
        """在下拉菜单中根据索引选择选项
        
        Args:
            locator: 下拉框元素定位器
            index: 选项的索引（从0开始）
        """
        try:
            log.info(f"在下拉框中选择索引：{locator}，索引：{index}")
            ele = self.find_element(locator, highlight=True)
            Select(ele).select_by_index(index)
            log.info(f"成功在下拉框中选择索引：{locator}，索引：{index}")
            return True
        except Exception as e:
            log.error(f"在下拉框中选择索引失败：{locator}，索引：{index}，异常信息：{str(e)}")
            self.shot_file(f"select_by_index_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def is_selected(self, locator):
        """验证元素是否选中
        
        Args:
            locator: 元素定位器
            
        Returns:
            bool: 选中返回 True，未选中返回 False
        """
        try:
            log.info(f"检查元素是否选中：{locator}")
            ele = self.find_element(locator, highlight=True)
            result = ele.is_selected()
            log.info(f"元素选中状态：{locator}，结果：{result}")
            return result
        except Exception as e:
            log.error(f"检查元素选中状态失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"is_selected_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, NoSuchFrameException))
    def switch_to_frame(self, locator):
        """切换到指定的frame

        Args:
            locator: frame元素定位器
        """
        try:
            log.info(f"切换到frame：{locator}")
            ele = self.find_element(locator, highlight=True)
            self.driver.switch_to.frame(ele)
            log.info(f"成功切换到frame：{locator}")
            return True
        except NoSuchFrameException as e:
            log.error(f"找不到frame：{locator}，异常信息：{str(e)}")
            self.shot_file(f"switch_to_frame_no_such_frame_{int(time.time())}")
            raise
        except Exception as e:
            log.error(f"切换到frame失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"switch_to_frame_error_{int(time.time())}")
            raise

    def switch_to_default_frame(self):
        """切换到默认frame（顶层）"""
        try:
            log.info("切换到默认frame")
            self.driver.switch_to.default_content()
            log.info("成功切换到默认frame")
            return True
        except Exception as e:
            log.error(f"切换到默认frame失败，异常信息：{str(e)}")
            self.shot_file(f"switch_to_default_frame_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def get_text(self, locator):
        """获取元素文本
        
        Args:
            locator: 元素定位器
            
        Returns:
            str: 元素文本内容
        """
        try:
            log.info(f"获取元素文本：{locator}")
            ele = self.find_element(locator, highlight=True)

            ele_text = ele.text

            # 如果通过text属性无法获取文本值,就通过元素属性value来获取
            if ele_text is None or ele_text == '':
                ele_text = ele.get_attribute('value')

            # 如果通过属性value无法获取文本值,就通过元素属性innerText来获取
            if ele_text is None or ele_text == '':
                ele_text = ele.get_attribute('innerText')

            # 如果通过属性innerText无法获取文本值,就通过元素属性textContent来获取
            if ele_text is None or ele_text == '':
                ele_text = ele.get_attribute('textContent')

            log.info(f"获取元素文本成功：{locator}，文本内容：{ele_text}")
            return ele_text
        except Exception as e:
            log.error(f"获取元素文本失败：{locator}，异常信息：{str(e)}")
            self.shot_file(f"get_text_error_{int(time.time())}")
            raise

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def upload_file(self, locator, upload_file_path):
        """上传文件
        
        Args:
            locator: 文件上传元素定位器
            upload_file_path: 要上传的文件路径
        """
        try:
            log.info(f"上传文件：{locator}，文件路径：{upload_file_path}")
            ele = self.find_element(locator, highlight=True)
            ele.clear()
            ele.send_keys(upload_file_path)
            log.info(f"成功上传文件：{locator}，文件路径：{upload_file_path}")
            return True
        except Exception as e:
            log.error(f"上传文件失败：{locator}，文件路径：{upload_file_path}，异常信息：{str(e)}")
            self.shot_file(f"upload_file_error_{int(time.time())}")
            raise

    @property
    def get_source(self):
        """获取页面源代码
        
        Returns:
            str: 页面的HTML源代码
        """
        try:
            log.info("获取页面源代码")
            source = self.driver.page_source
            return source
        except Exception as e:
            log.error(f"获取页面源代码失败：{str(e)}")
            self.shot_file(f"get_source_error_{int(time.time())}")
            return ""

    def get_page_title(self, wait_time=2):
        """获取当前页面的标题
        
        Args:
            wait_time: 等待时间（秒），防止页面未加载完成
            
        Returns:
            str: 页面标题
        """
        try:
            log.info("获取页面标题")
            if wait_time > 0:
                time.sleep(wait_time)  # 防止运行过快,点击还未执行完毕就获取后续的元素
            title = self.driver.title
            log.info(f"页面标题：{title}")
            return title
        except Exception as e:
            log.error(f"获取页面标题失败：{str(e)}")
            self.shot_file(f"get_page_title_error_{int(time.time())}")
            return ""

    @property
    def get_page_url(self):
        """获取当前页面的URL地址
        
        Returns:
            str: 当前页面的URL
        """
        try:
            log.info("获取当前页面URL")
            url = self.driver.current_url
            log.info(f"当前页面URL：{url}")
            return url
        except Exception as e:
            log.error(f"获取当前页面URL失败：{str(e)}")
            self.shot_file(f"get_page_url_error_{int(time.time())}")
            return ""

    def shot_file(self, prefix="error"):
        """截图，可指定前缀
        
        Args:
            prefix: 截图文件名前缀，默认为"error"
            
        Returns:
            tuple: (是否成功, 文件路径)
        """
        try:
            log.info(f"开始截图，前缀：{prefix}")
            file_name = f"{prefix}_{strftime(fmt='%Y-%m-%d_%H-%M-%S')}.png"
            file_path = os.path.join(path_manager.screen_path, file_name)

            # 确保截图目录存在
            os.makedirs(path_manager.screen_path, exist_ok=True)

            result = self.driver.save_screenshot(file_path)
            log.info(f"截图已保存，图片路径为：{file_path}")
            return result, file_path
        except Exception as e:
            log.error(f"截图失败：{str(e)}")
            return False, None

    def execute_js(self, js, *args):
        """执行JavaScript代码
        
        Args:
            js: 要执行的JavaScript代码
            *args: 传递给JavaScript的参数
            
        Returns:
            任何JavaScript返回的结果
        """
        try:
            log.info(f"执行JavaScript：{js}")
            result = self.driver.execute_script(js, *args)
            log.info("JavaScript执行成功")
            return result
        except Exception as e:
            log.error(f"执行JavaScript失败：{js}，异常信息：{str(e)}")
            self.shot_file(f"execute_js_error_{int(time.time())}")
            raise

    def add_cookie(self, cookie):
        """添加Cookie
        
        Args:
            cookie: 要添加的cookie字典，必须包含"name"和"value"键
        """
        try:
            log.info(f"添加Cookie：{cookie}")
            self.driver.add_cookie(cookie)
            log.info(f"添加Cookie成功：{cookie}")
            return True
        except Exception as e:
            log.error(f"添加Cookie失败：{cookie}，异常信息：{str(e)}")
            self.shot_file(f"add_cookie_error_{int(time.time())}")
            raise

    def get_cookies(self):
        """获取所有Cookies
        
        Returns:
            list: 所有cookie的列表
        """
        try:
            log.info("获取所有Cookies")
            cookies = self.driver.get_cookies()
            log.info(f"获取到 {len(cookies)} 个Cookies")
            return cookies
        except Exception as e:
            log.error(f"获取所有Cookies失败：{str(e)}")
            return []

    def get_cookie(self, name):
        """获取指定名称的Cookie
        
        Args:
            name: Cookie名称
            
        Returns:
            dict: 包含cookie信息的字典，如果不存在则返回None
        """
        try:
            log.info(f"获取Cookie，名称：{name}")
            cookie = self.driver.get_cookie(name)
            if cookie:
                log.info(f"获取Cookie成功，名称：{name}，值：{cookie}")
            else:
                log.info(f"未找到名称为 {name} 的Cookie")
            return cookie
        except Exception as e:
            log.error(f"获取Cookie失败，名称：{name}，异常信息：{str(e)}")
            return None

    def delete_cookie(self, name):
        """删除指定名称的Cookie
        
        Args:
            name: 要删除的Cookie名称
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"删除Cookie，名称：{name}")
            self.driver.delete_cookie(name)
            log.info(f"删除Cookie成功，名称：{name}")
            return True
        except Exception as e:
            log.error(f"删除Cookie失败，名称：{name}，异常信息：{str(e)}")
            return False

    def delete_all_cookies(self):
        """删除所有Cookies
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("删除所有Cookies")
            self.driver.delete_all_cookies()
            log.info("删除所有Cookies成功")
            return True
        except Exception as e:
            log.error(f"删除所有Cookies失败：{str(e)}")
            return False

    def forward(self):
        """前进到下一页
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("页面前进")
            self.driver.forward()
            log.info("页面前进成功")
            return True
        except Exception as e:
            log.error(f"页面前进失败：{str(e)}")
            self.shot_file(f"forward_error_{int(time.time())}")
            return False

    def back(self):
        """返回上一页
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("页面后退")
            self.driver.back()
            log.info("页面后退成功")
            return True
        except Exception as e:
            log.error(f"页面后退失败：{str(e)}")
            self.shot_file(f"back_error_{int(time.time())}")
            return False

    def refresh(self):
        """刷新当前页面
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("刷新当前页面")
            self.driver.refresh()
            log.info("当前页面刷新完成")
            return True
        except Exception as e:
            log.error(f"页面刷新失败：{str(e)}")
            self.shot_file(f"refresh_error_{int(time.time())}")
            return False

    def close(self):
        """关闭当前窗口
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("关闭当前窗口")
            self.driver.close()
            log.info("当前窗口已关闭")
            return True
        except Exception as e:
            log.error(f"关闭当前窗口失败：{str(e)}")
            self.shot_file(f"close_window_error_{int(time.time())}")
            return False

    def quit(self):
        """退出浏览器，关闭所有窗口和进程
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("退出浏览器")
            self.driver.quit()
            log.info("浏览器已完全退出")
            return True
        except Exception as e:
            log.error(f"退出浏览器失败：{str(e)}")
            return False

    def get_locator(self, locator_name):
        """从yaml文件中获取元素对象
        
        Args:
            locator_name: 元素名称
            
        Returns:
            Locator: 元素定位器对象
            
        Raises:
            KeyError: 元素定位未找到
            Exception: 其他异常
        """
        try:
            elements = ElementYamlUtils(self.__class__.__name__)
            locator = elements[locator_name]
            return locator
        except KeyError:
            log.error(f"元素定位未找到：{locator_name}，请检查YAML文件配置")
            raise
        except Exception as e:
            log.error(f"获取元素定位异常：{locator_name}，异常信息：{str(e)}")
            raise

    def wait_for_element_present(self, locator, timeout=None, poll_frequency=0.5) -> bool:
        """等待元素出现在DOM中
        
        Args:
            locator: 元素定位器
            timeout: 超时时间（秒），默认使用全局配置
            poll_frequency: 轮询频率（秒）
            
        Returns:
            bool: 元素存在返回True，超时返回False
        """
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            log.info(f"等待元素出现在DOM中：{str(locator)}，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.presence_of_element_located((locator.locator_type, locator.locator_value)))
            log.info(f"元素已出现在DOM中：{str(locator)}")
            return True
        except TimeoutException:
            log.warning(f"等待元素出现超时：{str(locator)}，等待时间：{timeout}秒")
            self.shot_file(f"wait_element_present_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待元素出现异常：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"wait_element_present_error_{int(time.time())}")
            return False

    def wait_for_element_visible(self, locator, timeout=None, poll_frequency=0.5) -> Union[bool, WebElement]:
        """等待元素可见（在DOM中且可见）
        
        Args:
            locator: 元素定位器
            timeout: 超时时间（秒），默认使用全局配置
            poll_frequency: 轮询频率（秒）
            
        Returns:
            bool: 元素可见返回True，超时返回False
        """
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            log.info(f"等待元素可见：{str(locator)}，超时时间：{timeout}秒")
            element = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.visibility_of_element_located((locator.locator_type, locator.locator_value)))
            log.info(f"元素已可见：{str(locator)}")
            return element
        except TimeoutException:
            log.warning(f"等待元素可见超时：{str(locator)}，等待时间：{timeout}秒")
            self.shot_file(f"wait_element_visible_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待元素可见异常：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"wait_element_visible_error_{int(time.time())}")
            return False

    def wait_for_element_clickable(self, locator, timeout=None, poll_frequency=0.5) -> bool:
        """等待元素可点击（在DOM中、可见且可交互）
        
        Args:
            locator: 元素定位器
            timeout: 超时时间（秒），默认使用全局配置
            poll_frequency: 轮询频率（秒）
            
        Returns:
            bool: 元素可点击返回True，超时返回False
        """
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            log.info(f"等待元素可点击：{str(locator)}，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.element_to_be_clickable((locator.locator_type, locator.locator_value)))
            log.info(f"元素已可点击：{str(locator)}")
            return True
        except TimeoutException:
            log.warning(f"等待元素可点击超时：{str(locator)}，等待时间：{timeout}秒")
            self.shot_file(f"wait_element_clickable_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待元素可点击异常：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"wait_element_clickable_error_{int(time.time())}")
            return False

    def wait_for_text_present(self, locator, text, timeout=None, poll_frequency=0.5) -> bool:
        """等待指定文本出现在元素中
        
        Args:
            locator: 元素定位器
            text: 要等待的文本内容
            timeout: 超时时间（秒），默认使用全局配置
            poll_frequency: 轮询频率（秒）
            
        Returns:
            bool: 文本出现返回True，超时返回False
        """
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            log.info(f"等待文本出现在元素中：{str(locator)}，文本：{text}，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.text_to_be_present_in_element((locator.locator_type, locator.locator_value), text))
            log.info(f"文本已出现在元素中：{str(locator)}，文本：{text}")
            return True
        except TimeoutException:
            log.warning(f"等待文本出现超时：{str(locator)}，文本：{text}，等待时间：{timeout}秒")
            self.shot_file(f"wait_text_present_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待文本出现异常：{str(locator)}，文本：{text}，异常信息：{str(e)}")
            self.shot_file(f"wait_text_present_error_{int(time.time())}")
            return False

    def wait_for_url_contains(self, text, timeout=None, poll_frequency=0.5) -> bool:
        """等待URL包含指定文本

        Args:
            text: 要等待的URL包含的文本
            timeout: 超时时间（秒），默认使用全局配置
            poll_frequency: 轮询频率（秒）

        Returns:
            bool: URL包含文本返回True，超时返回False
        """
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            log.info(f"等待URL包含文本：{text}，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.url_contains(text))
            current_url = self.driver.current_url
            log.info(f"URL已包含文本：{text}，当前URL：{current_url}")
            return True
        except TimeoutException:
            current_url = self.driver.current_url
            log.warning(f"等待URL包含文本超时：{text}，等待时间：{timeout}秒，当前URL：{current_url}")
            self.shot_file(f"wait_url_contains_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待URL包含文本异常：{text}，异常信息：{str(e)}")
            self.shot_file(f"wait_url_contains_error_{int(time.time())}")
            return False

    def wait_for_page_loaded(self, timeout=30) -> bool:
        """等待页面完全加载，包括DOM就绪和jQuery加载完成（如果存在）
        
        Args:
            timeout: 超时时间（秒），默认30秒
            
        Returns:
            bool: 页面加载完成返回True，超时返回False
        """
        try:
            current_url = self.driver.current_url
            log.info(f"等待页面完全加载，URL：{current_url}，超时时间：{timeout}秒")

            # 等待页面就绪状态
            log.info("等待页面DOM就绪状态...")
            WebDriverWait(self.driver, timeout).until(
                lambda d: d.execute_script('return document.readyState') == 'complete')
            log.info("页面DOM已就绪")

            # 等待jQuery加载完成（如果存在）
            jquery_exists = self.driver.execute_script('return typeof jQuery !== "undefined"')
            if jquery_exists:
                log.info("检测到jQuery，等待jQuery请求完成...")
                jquery_ready = self.driver.execute_script('return jQuery.active === 0')
                if not jquery_ready:
                    WebDriverWait(self.driver, timeout).until(
                        lambda d: d.execute_script('return jQuery.active === 0'))
                log.info("jQuery请求已完成")

            log.info(f"页面已完全加载，URL：{current_url}")
            return True
        except TimeoutException:
            current_url = self.driver.current_url
            log.warning(f"等待页面加载超时，URL：{current_url}，等待时间：{timeout}秒")
            self.shot_file(f"wait_page_loaded_timeout_{int(time.time())}")
            return False
        except Exception as e:
            current_url = self.driver.current_url
            log.error(f"等待页面加载异常，URL：{current_url}，异常信息：{str(e)}")
            self.shot_file(f"wait_page_loaded_error_{int(time.time())}")
            return False

    def is_element_present(self, locator, timeout=1) -> bool:
        """检查元素是否存在于DOM中（不一定可见）
        
        Args:
            locator: 元素定位器
            timeout: 超时时间（秒），默认1秒
            
        Returns:
            bool: 元素存在返回True，不存在返回False
        """
        try:
            log.info(f"检查元素是否存在：{str(locator)}，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout).until(
                EC.presence_of_element_located((locator.locator_type, locator.locator_value)))
            log.info(f"元素存在：{str(locator)}")
            return True
        except TimeoutException:
            log.info(f"元素不存在：{str(locator)}")
            return False
        except Exception as e:
            log.warning(f"检查元素是否存在时发生异常：{str(locator)}，异常信息：{str(e)}")
            return False

    def is_element_visible(self, locator, timeout=1) -> bool:
        """检查元素是否可见（在DOM中且可见）
        
        Args:
            locator: 元素定位器
            timeout: 超时时间（秒），默认1秒
            
        Returns:
            bool: 元素可见返回True，不可见返回False
        """
        try:
            log.info(f"检查元素是否可见：{str(locator)}，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout).until(
                EC.visibility_of_element_located((locator.locator_type, locator.locator_value)))
            log.info(f"元素可见：{str(locator)}")
            return True
        except TimeoutException:
            log.info(f"元素不可见：{str(locator)}")
            return False
        except Exception as e:
            log.warning(f"检查元素是否可见时发生异常：{str(locator)}，异常信息：{str(e)}")
            return False

    def is_element_clickable(self, locator, timeout=1) -> bool:
        """检查元素是否可点击（在DOM中、可见且可交互）
        
        Args:
            locator: 元素定位器
            timeout: 超时时间（秒），默认1秒
            
        Returns:
            bool: 元素可点击返回True，不可点击返回False
        """
        try:
            log.info(f"检查元素是否可点击：{str(locator)}，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout).until(
                EC.element_to_be_clickable((locator.locator_type, locator.locator_value)))
            log.info(f"元素可点击：{str(locator)}")
            return True
        except TimeoutException:
            log.info(f"元素不可点击：{str(locator)}")
            return False
        except Exception as e:
            log.warning(f"检查元素是否可点击时发生异常：{str(locator)}，异常信息：{str(e)}")
            return False

    def wait_for_element_staleness(self, element, timeout=None, poll_frequency=0.5) -> bool:
        """检查元素是否已经不再附加到DOM中，用于检查页面是否刷新
        
        Args:
            element: WebElement对象
            timeout: 超时时间（秒），默认使用全局配置
            poll_frequency: 轮询频率（秒）
            
        Returns:
            bool: 元素已移除返回True，超时返回False
        """
        try:
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            log.info(f"等待元素从DOM中移除或变陈旧，超时时间：{timeout}秒")
            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.staleness_of(element))
            log.info("元素已从DOM中移除或变陈旧")
            return True
        except TimeoutException:
            log.warning(f"等待元素从DOM移除超时，等待时间：{timeout}秒")
            self.shot_file(f"wait_element_staleness_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待元素从DOM移除异常：{str(e)}")
            self.shot_file(f"wait_element_staleness_error_{int(time.time())}")
            return False

    def accept_alert(self, timeout=5) -> bool:
        """接受弹窗（点击确定按钮）
        
        Args:
            timeout: 等待弹窗出现的超时时间（秒）
            
        Returns:
            bool: 成功接受弹窗返回True，未找到弹窗或异常返回False
        """
        try:
            log.info(f"等待弹窗出现，超时时间：{timeout}秒")
            self.wait_for_alert_is_present(timeout)
            alert = self.driver.switch_to.alert
            alert_text = alert.text
            log.info(f"弹窗已出现，文本内容：{alert_text}")
            alert.accept()
            log.info(f"已接受弹窗（点击确定按钮）")
            return True
        except TimeoutException:
            log.warning("等待弹窗超时，未找到弹窗")
            self.shot_file(f"accept_alert_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"处理弹窗异常：{str(e)}")
            self.shot_file(f"accept_alert_error_{int(time.time())}")
            return False

    def dismiss_alert(self, timeout=5) -> bool:
        """取消弹窗（点击取消按钮）
        
        Args:
            timeout: 等待弹窗出现的超时时间（秒）
            
        Returns:
            bool: 成功取消弹窗返回True，未找到弹窗或异常返回False
        """
        try:
            log.info(f"等待弹窗出现，超时时间：{timeout}秒")
            self.wait_for_alert_is_present(timeout)
            alert = self.driver.switch_to.alert
            alert_text = alert.text
            log.info(f"弹窗已出现，文本内容：{alert_text}")
            alert.dismiss()
            log.info(f"已取消弹窗（点击取消按钮）")
            return True
        except TimeoutException:
            log.warning("等待弹窗超时，未找到弹窗")
            self.shot_file(f"dismiss_alert_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"处理弹窗异常：{str(e)}")
            self.shot_file(f"dismiss_alert_error_{int(time.time())}")
            return False

    def get_alert_text(self, timeout=5) -> str:
        """获取弹窗文本
        
        Args:
            timeout: 等待弹窗出现的超时时间（秒）
            
        Returns:
            str: 弹窗文本，未找到弹窗或异常返回空字符串
        """
        try:
            log.info(f"等待弹窗出现以获取文本，超时时间：{timeout}秒")
            self.wait_for_alert_is_present(timeout)
            alert = self.driver.switch_to.alert
            alert_text = alert.text
            log.info(f"成功获取弹窗文本：{alert_text}")
            return alert_text
        except TimeoutException:
            log.warning("等待弹窗超时，未找到弹窗")
            self.shot_file(f"get_alert_text_timeout_{int(time.time())}")
            return ""
        except Exception as e:
            log.error(f"获取弹窗文本异常：{str(e)}")
            self.shot_file(f"get_alert_text_error_{int(time.time())}")
            return ""

    def scroll_to_element(self, locator, align_to_top=True) -> bool:
        """滚动到元素位置
        
        Args:
            locator: 元素定位器
            align_to_top: 是否将元素对齐到视图顶部，False则对齐到底部
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"滚动到元素位置：{str(locator)}，对齐到{'顶部' if align_to_top else '底部'}")
            element = self.find_element(locator, highlight=False)
            self.execute_js(
                'arguments[0].scrollIntoView(arguments[1]);',
                element,
                align_to_top
            )
            log.info(f"已滚动到元素位置：{str(locator)}")
            return True
        except Exception as e:
            log.error(f"滚动到元素位置异常：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"scroll_to_element_error_{int(time.time())}")
            return False

    def scroll_to_position(self, x=0, y=0) -> bool:
        """滚动到指定位置
        
        Args:
            x: 水平坐标
            y: 垂直坐标
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"滚动到指定位置：({x}, {y})")
            self.execute_js(f'window.scrollTo({x}, {y});')
            log.info(f"已滚动到位置：({x}, {y})")
            return True
        except Exception as e:
            log.error(f"滚动到位置异常：({x}, {y})，异常信息：{str(e)}")
            self.shot_file(f"scroll_to_position_error_{int(time.time())}")
            return False

    def scroll_to_bottom(self) -> bool:
        """滚动到页面底部
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("滚动到页面底部")
            self.execute_js('window.scrollTo(0, document.body.scrollHeight);')
            log.info("已滚动到页面底部")
            return True
        except Exception as e:
            log.error(f"滚动到页面底部异常：{str(e)}")
            self.shot_file(f"scroll_to_bottom_error_{int(time.time())}")
            return False

    def scroll_to_top(self) -> bool:
        """滚动到页面顶部
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("滚动到页面顶部")
            self.execute_js('window.scrollTo(0, 0);')
            log.info("已滚动到页面顶部")
            return True
        except Exception as e:
            log.error(f"滚动到页面顶部异常：{str(e)}")
            self.shot_file(f"scroll_to_top_error_{int(time.time())}")
            return False

    @retry(exceptions=(NoSuchFrameException, StaleElementReferenceException))
    def switch_to_frame_by_index(self, index=0) -> bool:
        """通过索引切换到iframe
        
        Args:
            index: iframe的索引，默认为0（第一个iframe）
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"切换到iframe，索引：{index}")
            self.driver.switch_to.frame(index)
            log.info(f"已切换到iframe，索引：{index}")
            return True
        except NoSuchFrameException as e:
            log.error(f"切换到iframe失败，索引：{index}，找不到iframe：{str(e)}")
            self.shot_file(f"switch_to_frame_by_index_no_such_frame_{int(time.time())}")
            raise
        except Exception as e:
            log.error(f"切换到iframe失败，索引：{index}，异常信息：{str(e)}")
            self.shot_file(f"switch_to_frame_by_index_error_{int(time.time())}")
            return False

    def switch_to_parent_frame(self) -> bool:
        """切换到父级iframe
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("切换到父级iframe")
            self.driver.switch_to.parent_frame()
            log.info("已切换到父级iframe")
            return True
        except Exception as e:
            log.error(f"切换到父级iframe失败：{str(e)}")
            return False

    def switch_to_default_content(self) -> bool:
        """切换到默认内容（退出所有iframe）
        
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info("切换到默认内容（退出所有iframe）")
            self.driver.switch_to.default_content()
            log.info("已切换到默认内容（退出所有iframe）")
            return True
        except Exception as e:
            log.error(f"切换到默认内容失败：{str(e)}")
            self.shot_file(f"switch_to_default_content_error_{int(time.time())}")
            return False

    def hover(self, locator, timeout=None) -> bool:
        """鼠标悬停在元素上
        
        Args:
            locator: 元素定位器
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"鼠标悬停在元素上：{str(locator)}")
            element = self.find_element(locator, timeout=timeout)
            actions = ActionChains(self.driver)
            actions.move_to_element(element).perform()
            log.info(f"已成功悬停在元素上：{str(locator)}")
            return True
        except TimeoutException as e:
            log.error(f"鼠标悬停在元素上超时：{str(locator)}，超时信息：{str(e)}")
            self.shot_file(f"hover_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"鼠标悬停在元素上失败：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"hover_error_{int(time.time())}")
            return False

    @retry(exceptions=(StaleElementReferenceException, ElementNotInteractableException))
    def drag_and_drop_by_offset(self, locator, x_offset, y_offset, timeout=None) -> bool:
        """拖拽元素到指定偏移位置
        
        Args:
            locator: 元素定位器
            x_offset: X轴偏移量
            y_offset: Y轴偏移量
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"拖拽元素到偏移位置：{str(locator)}，X偏移：{x_offset}，Y偏移：{y_offset}")
            element = self.find_element(locator, timeout=timeout)
            actions = ActionChains(self.driver)
            actions.drag_and_drop_by_offset(element, x_offset, y_offset).perform()
            log.info(f"已成功拖拽元素到偏移位置：{str(locator)}，X偏移：{x_offset}，Y偏移：{y_offset}")
            return True
        except TimeoutException as e:
            log.error(f"拖拽元素到偏移位置超时：{str(locator)}，X偏移：{x_offset}，Y偏移：{y_offset}，超时信息：{str(e)}")
            self.shot_file(f"drag_and_drop_by_offset_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"拖拽元素到偏移位置失败：{str(locator)}，X偏移：{x_offset}，Y偏移：{y_offset}，异常信息：{str(e)}")
            self.shot_file(f"drag_and_drop_by_offset_error_{int(time.time())}")
            return False

    def get_element_attribute(self, locator, attribute, timeout=None) -> str:
        """获取元素属性值
        
        Args:
            locator: 元素定位器
            attribute: 属性名称
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            str: 属性值，如果获取失败则返回空字符串
        """
        try:
            log.info(f"获取元素属性值：{str(locator)}，属性：{attribute}")
            element = self.find_element(locator, timeout=timeout)
            value = element.get_attribute(attribute)
            log.info(f"获取元素属性值成功：{str(locator)}，属性：{attribute}，值：{value}")
            return value if value else ""
        except TimeoutException as e:
            log.error(f"获取元素属性值超时：{str(locator)}，属性：{attribute}，超时信息：{str(e)}")
            self.shot_file(f"get_element_attribute_timeout_{int(time.time())}")
            return ""
        except Exception as e:
            log.error(f"获取元素属性值失败：{str(locator)}，属性：{attribute}，异常信息：{str(e)}")
            self.shot_file(f"get_element_attribute_error_{int(time.time())}")
            return ""

    def get_element_property(self, locator, property_name, timeout=None) -> str:
        """获取元素属性值（使用property而非attribute）
        
        Args:
            locator: 元素定位器
            property_name: 属性名称
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            str: 属性值，如果获取失败则返回空字符串
        """
        try:
            log.info(f"获取元素property值：{str(locator)}，属性：{property_name}")
            element = self.find_element(locator, timeout=timeout)
            value = element.get_property(property_name)
            log.info(f"获取元素property值成功：{str(locator)}，属性：{property_name}，值：{value}")
            return str(value) if value is not None else ""
        except TimeoutException as e:
            log.error(f"获取元素property值超时：{str(locator)}，属性：{property_name}，超时信息：{str(e)}")
            self.shot_file(f"get_element_property_timeout_{int(time.time())}")
            return ""
        except Exception as e:
            log.error(f"获取元素property值失败：{str(locator)}，属性：{property_name}，异常信息：{str(e)}")
            self.shot_file(f"get_element_property_error_{int(time.time())}")
            return ""

    def get_element_css_value(self, locator, css_property, timeout=None) -> str:
        """获取元素CSS值
        
        Args:
            locator: 元素定位器
            css_property: CSS属性名称
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            str: CSS属性值，如果获取失败则返回空字符串
        """
        try:
            log.info(f"获取元素CSS值：{str(locator)}，CSS属性：{css_property}")
            element = self.find_element(locator, timeout=timeout)
            value = element.value_of_css_property(css_property)
            log.info(f"获取元素CSS值成功：{str(locator)}，CSS属性：{css_property}，值：{value}")
            return value if value else ""
        except TimeoutException as e:
            log.error(f"获取元素CSS值超时：{str(locator)}，CSS属性：{css_property}，超时信息：{str(e)}")
            self.shot_file(f"get_element_css_value_timeout_{int(time.time())}")
            return ""
        except Exception as e:
            log.error(f"获取元素CSS值失败：{str(locator)}，CSS属性：{css_property}，异常信息：{str(e)}")
            self.shot_file(f"get_element_css_value_error_{int(time.time())}")
            return ""

    def get_element_location(self, locator, timeout=None) -> dict:
        """获取元素位置
        
        Args:
            locator: 元素定位器
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            dict: 包含x和y坐标的字典，如果获取失败则返回{"x": 0, "y": 0}
        """
        try:
            log.info(f"获取元素位置：{str(locator)}")
            element = self.find_element(locator, timeout=timeout)
            location = element.location
            log.info(f"获取元素位置成功：{str(locator)}，位置：{location}")
            return location
        except TimeoutException as e:
            log.error(f"获取元素位置超时：{str(locator)}，超时信息：{str(e)}")
            self.shot_file(f"get_element_location_timeout_{int(time.time())}")
            return {"x": 0, "y": 0}
        except Exception as e:
            log.error(f"获取元素位置失败：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"get_element_location_error_{int(time.time())}")
            return {"x": 0, "y": 0}

    def get_element_size(self, locator, timeout=None) -> dict:
        """获取元素大小
        
        Args:
            locator: 元素定位器
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            dict: 包含width和height的字典，如果获取失败则返回{"width": 0, "height": 0}
        """
        try:
            log.info(f"获取元素大小：{str(locator)}")
            element = self.find_element(locator, timeout=timeout)
            size = element.size
            log.info(f"获取元素大小成功：{str(locator)}，大小：{size}")
            return size
        except TimeoutException as e:
            log.error(f"获取元素大小超时：{str(locator)}，超时信息：{str(e)}")
            self.shot_file(f"get_element_size_timeout_{int(time.time())}")
            return {"width": 0, "height": 0}
        except Exception as e:
            log.error(f"获取元素大小失败：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"get_element_size_error_{int(time.time())}")
            return {"width": 0, "height": 0}

    def get_element_rect(self, locator, timeout=None) -> dict:
        """获取元素矩形区域（位置和大小）
        
        Args:
            locator: 元素定位器
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            dict: 包含x、y、width和height的字典，如果获取失败则返回{"x": 0, "y": 0, "width": 0, "height": 0}
        """
        try:
            log.info(f"获取元素矩形区域：{str(locator)}")

            element = self.find_element(locator, timeout=timeout)
            rect = element.rect
            log.info(f"获取元素矩形区域成功：{str(locator)}，区域：{rect}")
            return rect
        except TimeoutException as e:
            log.error(f"获取元素矩形区域超时：{str(locator)}，超时信息：{str(e)}")
            self.shot_file(f"get_element_rect_timeout_{int(time.time())}")
            return {"x": 0, "y": 0, "width": 0, "height": 0}
        except Exception as e:
            log.error(f"获取元素矩形区域失败：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"get_element_rect_error_{int(time.time())}")
            return {"x": 0, "y": 0, "width": 0, "height": 0}

    def element_screenshot(self, locator, filename=None, timeout=None) -> str:
        """截取元素截图
        
        Args:
            locator: 元素定位器
            filename: 截图文件名，默认为None，会自动生成文件名
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            str: 截图文件路径，如果截图失败则返回空字符串
        """
        try:
            log.info(f"截取元素截图：{str(locator)}，文件名：{filename if filename else '自动生成'}")
            element = self.find_element(locator, timeout=timeout)
            if filename is None:
                filename = f"element_{int(time.time())}.png"

            # 确保截图目录存在
            screenshot_dir = os.path.dirname(filename)
            if screenshot_dir and not os.path.exists(screenshot_dir):
                os.makedirs(screenshot_dir)

            element.screenshot(filename)
            log.info(f"截取元素截图成功：{str(locator)}，保存为：{filename}")
            return filename
        except TimeoutException as e:
            log.error(f"截取元素截图超时：{str(locator)}，超时信息：{str(e)}")
            self.shot_file(f"element_screenshot_timeout_{int(time.time())}")
            return ""
        except Exception as e:
            log.error(f"截取元素截图失败：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"element_screenshot_error_{int(time.time())}")
            return ""

    def press_key(self, key) -> bool:
        """按下键盘按键
        
        Args:
            key: 键盘按键，可以是Keys类中定义的按键
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"按下键盘按键：{key}")
            actions = ActionChains(self.driver)
            actions.send_keys(key).perform()
            log.info(f"已成功按下键盘按键：{key}")
            return True
        except Exception as e:
            log.error(f"按下键盘按键失败：{key}，异常信息：{str(e)}")
            self.shot_file(f"press_key_error_{int(time.time())}")
            return False

    def press_keys(self, locator, *keys, timeout=None) -> bool:
        """在元素上按下多个键盘按键
        
        Args:
            locator: 元素定位器
            *keys: 多个键盘按键，可以是Keys类中定义的按键
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"在元素上按下多个键盘按键：{str(locator)}，按键：{keys}")
            element = self.find_element(locator, timeout=timeout)
            actions = ActionChains(self.driver)
            actions.click(element)
            for key in keys:
                actions.send_keys(key)
            actions.perform()
            log.info(f"已成功在元素上按下多个键盘按键：{str(locator)}，按键：{keys}")
            return True
        except TimeoutException as e:
            log.error(f"在元素上按下多个键盘按键超时：{str(locator)}，按键：{keys}，超时信息：{str(e)}")
            self.shot_file(f"press_keys_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"在元素上按下多个键盘按键失败：{str(locator)}，按键：{keys}，异常信息：{str(e)}")
            self.shot_file(f"press_keys_error_{int(time.time())}")
            return False

    def select_by_visible_text(self, locator, text, timeout=None) -> bool:
        """通过可见文本选择下拉框选项
        
        Args:
            locator: 元素定位器
            text: 可见文本
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"通过可见文本选择下拉框选项：{str(locator)}，文本：{text}")
            element = self.find_element(locator, timeout=timeout)
            select = Select(element)
            select.select_by_visible_text(text)
            log.info(f"已成功通过可见文本选择下拉框选项：{str(locator)}，文本：{text}")
            return True
        except TimeoutException as e:
            log.error(f"通过可见文本选择下拉框选项超时：{str(locator)}，文本：{text}，超时信息：{str(e)}")
            self.shot_file(f"select_by_visible_text_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"通过可见文本选择下拉框选项失败：{str(locator)}，文本：{text}，异常信息：{str(e)}")
            self.shot_file(f"select_by_visible_text_error_{int(time.time())}")
            return False

    def select_by_value(self, locator, value, timeout=None) -> bool:
        """通过值选择下拉框选项
        
        Args:
            locator: 元素定位器
            value: 选项的value属性值
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"通过值选择下拉框选项：{str(locator)}，值：{value}")
            element = self.find_element(locator, timeout=timeout)
            select = Select(element)
            select.select_by_value(value)
            log.info(f"已成功通过值选择下拉框选项：{str(locator)}，值：{value}")
            return True
        except TimeoutException as e:
            log.error(f"通过值选择下拉框选项超时：{str(locator)}，值：{value}，超时信息：{str(e)}")
            self.shot_file(f"select_by_value_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"通过值选择下拉框选项失败：{str(locator)}，值：{value}，异常信息：{str(e)}")
            self.shot_file(f"select_by_value_error_{int(time.time())}")
            return False

    def get_selected_option_text(self, locator, timeout=None) -> str:
        """获取下拉框已选择选项的文本
        
        Args:
            locator: 元素定位器
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            str: 已选择选项的文本，失败返回空字符串
        """
        try:
            log.info(f"获取下拉框已选择选项的文本：{str(locator)}")
            element = self.find_element(locator, timeout=timeout)
            select = Select(element)
            selected_option = select.first_selected_option
            text = selected_option.text
            log.info(f"已成功获取下拉框已选择选项的文本：{str(locator)}，文本：{text}")
            return text
        except TimeoutException as e:
            log.error(f"获取下拉框已选择选项的文本超时：{str(locator)}，超时信息：{str(e)}")
            self.shot_file(f"get_selected_option_text_timeout_{int(time.time())}")
            return ""
        except Exception as e:
            log.error(f"获取下拉框已选择选项的文本失败：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"get_selected_option_text_error_{int(time.time())}")
            return ""

    def get_all_options(self, locator, timeout=None) -> list:
        """获取下拉框所有选项
        
        Args:
            locator: 元素定位器
            timeout: 超时时间，默认为None，使用默认超时时间
            
        Returns:
            list: 所有选项的文本列表，失败返回空列表
        """
        try:
            log.info(f"获取下拉框所有选项：{str(locator)}")
            element = self.find_element(locator, timeout=timeout)
            select = Select(element)
            options = select.options
            option_texts = [option.text for option in options]
            log.info(f"已成功获取下拉框所有选项：{str(locator)}，选项数量：{len(option_texts)}")
            return option_texts
        except TimeoutException as e:
            log.error(f"获取下拉框所有选项超时：{str(locator)}，超时信息：{str(e)}")
            self.shot_file(f"get_all_options_timeout_{int(time.time())}")
            return []
        except Exception as e:
            log.error(f"获取下拉框所有选项失败：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"get_all_options_error_{int(time.time())}")
            return []

    def wait_for_text_to_be_present_in_element_value(self, locator, text, timeout=None, poll_frequency=0.5) -> bool:
        """等待元素值中出现指定文本
        
        Args:
            locator: 元素定位器
            text: 要等待的文本
            timeout: 超时时间，默认为None，使用默认超时时间
            poll_frequency: 轮询频率，默认为0.5秒
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"等待元素值中出现指定文本：{str(locator)}，文本：{text}")
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.text_to_be_present_in_element_value(
                    (locator.locator_type, locator.locator_value), text))
            log.info(f"已成功等待元素值中出现指定文本：{str(locator)}，文本：{text}")
            return True
        except TimeoutException:
            log.warning(f"等待元素值中出现指定文本超时：{str(locator)}，文本：{text}，等待时间：{timeout}秒")
            self.shot_file(f"wait_for_text_in_element_value_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待元素值中出现指定文本异常：{str(locator)}，文本：{text}，异常信息：{str(e)}")
            self.shot_file(f"wait_for_text_in_element_value_error_{int(time.time())}")
            return False

    def wait_for_number_of_windows_to_be(self, number, timeout=None, poll_frequency=0.5) -> bool:
        """等待窗口数量为指定值
        
        Args:
            number: 期望的窗口数量
            timeout: 超时时间，默认为None，使用默认超时时间
            poll_frequency: 轮询频率，默认为0.5秒
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"等待窗口数量为指定值：{number}")
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.number_of_windows_to_be(number))
            log.info(f"已成功等待窗口数量为指定值：{number}")
            return True
        except TimeoutException:
            log.warning(f"等待窗口数量为指定值超时：{number}，等待时间：{timeout}秒")
            self.shot_file(f"wait_for_number_of_windows_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待窗口数量为指定值异常：{number}，异常信息：{str(e)}")
            self.shot_file(f"wait_for_number_of_windows_error_{int(time.time())}")
            return False

    def wait_for_frame_to_be_available_and_switch_to_it(self, locator, timeout=None, poll_frequency=0.5) -> bool:
        """等待frame可用并切换到它
        
        Args:
            locator: 元素定位器
            timeout: 超时时间，默认为None，使用默认超时时间
            poll_frequency: 轮询频率，默认为0.5秒
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"等待frame可用并切换到它：{str(locator)}")
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.frame_to_be_available_and_switch_to_it(
                    (locator.locator_type, locator.locator_value)))
            log.info(f"已成功等待frame可用并切换到它：{str(locator)}")
            return True
        except TimeoutException:
            log.warning(f"等待frame可用并切换到它超时：{str(locator)}，等待时间：{timeout}秒")
            self.shot_file(f"wait_for_frame_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待frame可用并切换到它异常：{str(locator)}，异常信息：{str(e)}")
            self.shot_file(f"wait_for_frame_error_{int(time.time())}")
            return False

    def wait_for_element_selection_state_to_be(self, locator, is_selected, timeout=None, poll_frequency=0.5) -> bool:
        """等待元素选中状态为指定值

        Args:
            locator: 元素定位器
            is_selected: 期望的选中状态，True为选中，False为未选中
            timeout: 超时时间，默认为None，使用默认超时时间
            poll_frequency: 轮询频率，默认为0.5秒

        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"等待元素选中状态为指定值：{str(locator)}，选中状态：{is_selected}")
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.element_selection_state_to_be(
                    (locator.locator_type, locator.locator_value), is_selected))
            log.info(f"已成功等待元素选中状态为指定值：{str(locator)}，选中状态：{is_selected}")
            return True
        except TimeoutException:
            log.warning(f"等待元素选中状态为指定值超时：{str(locator)}，选中状态：{is_selected}，等待时间：{timeout}秒")
            self.shot_file(f"wait_for_element_selection_state_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待元素选中状态为指定值异常：{str(locator)}，选中状态：{is_selected}，异常信息：{str(e)}")
            self.shot_file(f"wait_for_element_selection_state_error_{int(time.time())}")
            return False

    def wait_for_element_located_selection_state_to_be(self, locator, is_selected, timeout=None,
                                                       poll_frequency=0.5) -> bool:
        """等待定位的元素选中状态为指定值
        
        Args:
            locator: 元素定位器
            is_selected: 期望的选中状态，True为选中，False为未选中
            timeout: 超时时间，默认为None，使用默认超时时间
            poll_frequency: 轮询频率，默认为0.5秒
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"等待定位的元素选中状态为指定值：{str(locator)}，选中状态：{is_selected}")
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.element_located_selection_state_to_be(
                    (locator.locator_type, locator.locator_value), is_selected))
            log.info(f"已成功等待定位的元素选中状态为指定值：{str(locator)}，选中状态：{is_selected}")
            return True
        except TimeoutException:
            log.warning(
                f"等待定位的元素选中状态为指定值超时：{str(locator)}，选中状态：{is_selected}，等待时间：{timeout}秒")
            self.shot_file(f"wait_for_element_located_selection_state_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待定位的元素选中状态为指定值异常：{str(locator)}，选中状态：{is_selected}，异常信息：{str(e)}")
            self.shot_file(f"wait_for_element_located_selection_state_error_{int(time.time())}")
            return False

    def wait_for_alert_is_present(self, timeout=None, poll_frequency=0.5) -> bool:
        """等待弹窗出现
        
        Args:
            timeout: 超时时间，默认为None，使用默认超时时间
            poll_frequency: 轮询频率，默认为0.5秒
            
        Returns:
            bool: 成功返回True，失败返回False
        """
        try:
            log.info(f"等待弹窗出现")
            if timeout is None:
                timeout = path_manager.explicitly_wait_time

            WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
                EC.alert_is_present())
            log.info("已成功等待弹窗出现")
            return True
        except TimeoutException:
            log.warning(f"等待弹窗出现超时，等待时间：{timeout}秒")
            self.shot_file(f"wait_for_alert_timeout_{int(time.time())}")
            return False
        except Exception as e:
            log.error(f"等待弹窗出现异常：{str(e)}")
            self.shot_file(f"wait_for_alert_error_{int(time.time())}")
            return False
